Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Sep 4, 2025

Summary

This PR fixes a critical trust protection issue where files were being saved to disk before the user clicked the "Save" button when the PREVENT_FOCUS_DISRUPTION experiment is enabled.

Problem

As reported in #7683, when Auto-Approve Write is disabled and a user asks Roo to write something to a file, the file was being created on disk immediately, even before the user clicked "Save". This bypassed the trust protection mechanism designed to give users control over file writes.

Solution

The fix reorders the operations in the writeToFileTool to ensure that:

  1. File preparation (setting up diffViewProvider properties) happens BEFORE asking for approval
  2. The actual file save (saveDirectly) only happens AFTER user approval
  3. If the user rejects, the diffViewProvider state is properly cleaned up

Changes

  • src/core/tools/writeToFileTool.ts: Moved saveDirectly call to occur after user approval
  • src/core/tools/tests/writeToFileTool.spec.ts: Added comprehensive tests to verify the fix

Testing

  • ✅ All existing tests pass
  • ✅ Added new tests specifically for the PREVENT_FOCUS_DISRUPTION experiment behavior
  • ✅ Verified that files are NOT saved before user approval
  • ✅ Verified that files ARE saved after user approval
  • ✅ Verified proper state cleanup when user rejects

Security Impact

This fix strengthens the security posture by properly enforcing trust boundaries and ensuring no file writes occur without explicit user consent.

Fixes #7683


Important

Fixes file save order in writeToFileTool.ts to require user approval before saving when PREVENT_FOCUS_DISRUPTION is enabled.

  • Behavior:
    • In writeToFileTool.ts, moved saveDirectly to occur after user approval when PREVENT_FOCUS_DISRUPTION is enabled.
    • Ensures diffViewProvider properties are set up before approval request and reset if approval is denied.
  • Testing:
    • Added tests in writeToFileTool.spec.ts to verify files are not saved before approval and are saved after approval.
    • Tests ensure proper state cleanup when user rejects approval.
  • Security:
    • Strengthens trust protection by enforcing user consent before file writes.

This description was created by Ellipsis for cf6b384. You can customize this summary. It will automatically update as commits are pushed.

…TION is enabled

- Move saveDirectly call to occur AFTER user approval in writeToFileTool
- Reset diffViewProvider state when user rejects approval
- Add tests to verify files are not saved before approval
- Fixes issue where files were being written to disk before user clicked Save

Fixes #7683
@roomote roomote bot requested review from cte, jr and mrubens as code owners September 4, 2025 23:51
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Sep 4, 2025
Copy link
Contributor Author

@roomote roomote bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing my own code is like debugging in a mirror - everything looks backwards but the bugs are still mine.

}
}

// Set up diffViewProvider properties needed for saveDirectly BEFORE asking for approval
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment explains WHAT we're doing but not WHY it's critical for security. Could we make this more explicit? Something like:

Suggested change
// Set up diffViewProvider properties needed for saveDirectly BEFORE asking for approval
// Set up diffViewProvider properties needed for saveDirectly BEFORE asking for approval
// This ensures we have the original content for comparison but critically prevents any
// file writes from occurring until the user explicitly approves the operation


// Set up diffViewProvider properties needed for saveDirectly BEFORE asking for approval
// This ensures we have the original content for comparison but don't write anything yet
cline.diffViewProvider.editType = fileExists ? "modify" : "create"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice this block (lines 207-213) duplicates similar logic from the non-experiment path. Could we extract this into a helper function to reduce duplication? Something like prepareDiffViewState(fileExists, relPath)?

cline.diffViewProvider.editType = fileExists ? "modify" : "create"
if (fileExists) {
const absolutePath = path.resolve(cline.cwd, relPath)
cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add error handling here in case fs.readFile fails? Currently if reading the original file fails, the error would bubble up and might not be handled gracefully.


if (!didApprove) {
// Reset the diffViewProvider state since we're not proceeding
cline.diffViewProvider.editType = undefined
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good defensive programming! Properly resetting the state when the user rejects ensures no lingering state that could cause issues.

expect(mockCline.diffViewProvider.originalContent).toBe(undefined)
})

it("should save file AFTER user approval when experiment is enabled", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great test coverage for the happy path! Consider adding a test case for when fs.readFile fails to ensure we handle that error scenario gracefully.

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Sep 5, 2025
@bnmaxfield
Copy link

I'm unfamiliar with this code but this PR doesn't seem to be materially changing the save order. It just moves the diff viewer to show earlier. I doubt this will fix my bug.

@daniel-lxs daniel-lxs moved this from Triage to PR [Needs Prelim Review] in Roo Code Roadmap Sep 5, 2025
@hannesrudolph hannesrudolph added PR - Needs Preliminary Review and removed Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. labels Sep 5, 2025
@daniel-lxs
Copy link
Member

Closing, see #7683 (comment)

@daniel-lxs daniel-lxs closed this Sep 5, 2025
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Sep 5, 2025
@github-project-automation github-project-automation bot moved this from PR [Needs Prelim Review] to Done in Roo Code Roadmap Sep 5, 2025
@daniel-lxs daniel-lxs deleted the fix/prevent-premature-file-save-7683 branch September 5, 2025 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working PR - Needs Preliminary Review size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Roo saving file before User clicks "Save"

5 participants